﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.IO;
using System.ComponentModel;
using System.Data.SqlClient;
using SqlServerDataExporter.Properties;

namespace SqlServerDataExporter
{
    public class ViewModel : INotifyPropertyChanged
    {
        private string _connectionString = "Server=(local);Database=MyDataBase;Trusted_Connection=true;";

        private string _insertStatements = string.Empty;

        private string _fileName = string.Empty;

        private bool _exportTofile;

        private int _selectedTab;

        private bool _trustedConnection;

        private int _rowsExported;

        private TableItem _selectedTable;

        private SqlDataExporter _exporter = new SqlDataExporter();

        private ObservableCollection<TableItem> _tables = new ObservableCollection<TableItem>();

        private ObservableCollection<Column> _columns = new ObservableCollection<Column>();

        public event PropertyChangedEventHandler PropertyChanged;

        private string _error;

        private string _errorInApp;

        private BackgroundWorker _backGroundWorker;

        private string _exportText = "";

        private const string START_EXPORT = "Export";

        private const string STOP_EXPORT = "Cancel export";

        private string _binaryHexString = "";


        public string BinaryHexString
        {
            get { 
                return this._binaryHexString; 
            }
            set { 
                this._binaryHexString = value;
                NotifyPropChanged("BinaryHexString");
            }
        }

        public string ExportText
        {
            get
            {
                return this._exportText;
            }
            set { 
                this._exportText = value;
                NotifyPropChanged("ExportText");
            }

        }


        public string ErrorInApp
        {

            get { return this._errorInApp; }

            set { this._errorInApp = value;
               NotifyPropChanged("ErrorInApp");
            }
        }


        public string Database { get; set; }
        public string Server { get; set; }
        public string User { get; set; }
        public string Password { get; set; }

        public IList<TableItem> Tables
        {
            get { return _tables; }
        }

        public bool TrustedConnection
        {
            get
            {
                return this._trustedConnection;
            }
            set
            {
                this._trustedConnection = value;
                NotifyPropChanged("TrustedConnection");
                NotifyPropChanged("IsUsernamePasswordEnabled");
            }
        }

        public bool IsUsernamePasswordEnabled
        {
            get
            {
                return !TrustedConnection;
            }
        }
       

        /// <summary>
        /// default constructor.
        /// </summary>
        public ViewModel()
        {
            this._backGroundWorker = new BackgroundWorker();
            _backGroundWorker.WorkerReportsProgress = true;
            _backGroundWorker.DoWork += this.DoExport;
            _backGroundWorker.ProgressChanged += this.ProgressChanged;
            _backGroundWorker.RunWorkerCompleted += this.ExportCompleted;
            _backGroundWorker.WorkerSupportsCancellation = true;
            _exportText = START_EXPORT;
            Settings settings = Settings.Default;
            this.ConnectionString = settings.ConnectionString;
        }


        /// <summary>
        /// Saves UserSettings.
        /// </summary>
        public void SaveSettings()
        {
            Settings settings = Settings.Default;
            settings.ConnectionString = this.ConnectionString;
            settings.Save();
        }

        /// <summary>
        /// Exports the table to a file.
        /// Do not change UI here!
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DoExport(object sender,  DoWorkEventArgs e)
        {

            ReportProgress rp = new ReportProgress(_backGroundWorker);

            this._error = "";
            try
            {
                ExportData(rp);
            }
            catch (Exception ex)
            {
                this._error = "Error: " +  ex.Message;
            }
        }


        /// <summary>
        ///  ProgressChanged of the background-worker.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            this.RowsExported = e.ProgressPercentage;
        }

        private void ExportCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.ErrorInApp = this._error;
            NotifyPropChanged("InsertStatements");
            this.ExportText = START_EXPORT;
        }
        

        public int RowsExported
        {
            get { return this._rowsExported; }
            set
            {
                this._rowsExported = value;
                NotifyPropChanged("RowsExported");
            }
        }


        public void StartExport(bool? exporttoFile)
        {
            ErrorInApp = "";
            InsertStatements = "";
            if (exporttoFile.HasValue)
            {
                ExportToFile = exporttoFile.Value;
            }
            else
            {
                ExportToFile = false;
            }
            if (_backGroundWorker.IsBusy != true)
            {
                // Start the asynchronous operation.
                ExportText = STOP_EXPORT;
                _backGroundWorker.RunWorkerAsync();
            }
            else
            {
                _backGroundWorker.CancelAsync();
            }
        }


        public IList<Column> Columns
        {
            get { return _columns; }
        }


        public TableItem SelectedTable
        {
            get
            {
                return this._selectedTable;
            }
            set
            {
                this._selectedTable = value;
                NotifyPropChanged("SelectedTable");
                ShowColumns(this._selectedTable);
               
            }
        }

        public void CreateConnectionString()
        {
            this.ConnectionString = this._exporter.CreateConnectionString(this.Server, this.Database, this.User,
                                                                          this.Password, this.TrustedConnection);                        
        }

        public void ShowColumns(TableItem table)
        {
            this.Columns.Clear();
            if (table != null)
            {
                if (table.Columns == null)
                {
                    table.Columns = this._exporter.GetColumns(table);
                }
                foreach (Column col in table.Columns)
                {
                    this.Columns.Add(col);
                }
            }
        }

        public int SelectedTabIndex
        {
            get
            {
                return this._selectedTab;
            }
            set
            {
                this._selectedTab = value;
                NotifyPropChanged("SelectedTabIndex");
            }
        }


        /// <summary>
        /// Connecton String
        /// </summary>
        public String ConnectionString
        {
            get
            {
                return _connectionString;
            }
            set
            {
                this._connectionString = value;
                NotifyPropChanged("ConnectionString");
            }
        }

        public string FileName
        {

            get { return this._fileName; }
            set { 
                this._fileName = value;
                this.NotifyPropChanged("FileName");
            }
        }


        public bool ExportToFile
        {
            get { return this._exportTofile; }
            set { this._exportTofile = value; }
        }


        public String InsertStatements
        {

            get
            {
                return this._insertStatements;
            }
            set
            {
                this._insertStatements = value;
                NotifyPropChanged("InsertStatements");
            }

        }

        /// <summary>
        /// Connects with the database.
        /// May throw an exception.
        /// </summary>
        public void Connect()
        {
            _exporter.Connect(this._connectionString);
            IList<TableItem> tables = _exporter.ListTables();
            _tables.Clear();
            foreach (TableItem item in tables)
            {
                _tables.Add(item);
            }
            NextTab();
        }


        /// <summary>
        /// Select next tab.
        /// </summary>
        public void NextTab()
        {
            this.SelectedTabIndex++;
        }


        /// <summary>
        ///  Exports all data. Do not change UI in this method!
        /// </summary>
        private void ExportData(IReportProgress rp)
        {
            if (this.SelectedTable != null)
            {
                this.SelectedTable.Selected = true;
                Stream stream = new MemoryStream();
                if (ExportToFile)
                {
                    stream = new FileStream(this.FileName, FileMode.Create);
                }
                try
                {
                    _exporter.ExportTable(this.Tables, stream, rp);

                    if (!ExportToFile)
                    {
                        StreamReader tr = new StreamReader(stream);
                        stream.Seek(0, SeekOrigin.Begin);
                        _insertStatements = tr.ReadToEnd();
                    }
                }
                finally
                {
                    rp.ReportLastItem();
                    this.SelectedTable.Selected = false;
                    stream.Close();     
                }               
               
            }
        }

        /// <summary>
        /// Saves the content of BinaryHexString to the file as binary data.
        /// </summary>
        /// <param name="file"></param>
        public void SaveHexStringAsBinary(string file)
        {
            HexStringConverter.SaveHexString(file, this.BinaryHexString);
        }

        /// <summary>
        /// Loads a binary file as a hex string.
        /// </summary>
        /// <param name="file"></param>
        public void LoadBinaryAsHexString(string file)
        {
            this.BinaryHexString = HexStringConverter.LoadFromFile(file);
        }

        /// <summary>
        /// Fire a change in the viewmodel.
        /// </summary>
        /// <param name="propertyName"></param>
        private void NotifyPropChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                PropertyChangedEventArgs args = new PropertyChangedEventArgs(propertyName);
                this.PropertyChanged(this, args);
            }
        }
    }
}
